package org.lboot.ufs.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.crypto.SecureUtil;

import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lboot.core.exception.BusinessException;
import org.lboot.s3.client.S3Client;
import org.lboot.s3.config.OssProperties;
import org.lboot.ufs.entity.UfsFile;
import org.lboot.ufs.event.UfsFileUpdateEvent;
import org.lboot.ufs.params.FileUploadParams;
import org.lboot.ufs.service.UfsCtl;
import org.lboot.ufs.service.UfsFileService;
import org.lboot.ufs.utils.UfsUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;

@Slf4j
@Service
@AllArgsConstructor
public class UfsCtlImpl implements UfsCtl {

    UfsFileService fileService;

    OssProperties properties;

    S3Client s3Client;

    @Resource
    ApplicationContext context;

    @SneakyThrows
    private UfsFile upload(FileUploadParams params, String prefix) {
        String bucketName = params.getBucketName();
        if (Validator.isEmpty(params.getFile())){
            throw new BusinessException(HttpStatus.BAD_REQUEST,"上传文件不可为空");
        }
        // 简单初始化
        UfsFile file = UfsUtil.buildFile(params.getFile());
        // 存储模式
        file.setStorageMode(properties.getMode());
        // 如果文件名不为空
        if (Validator.isNotEmpty(params.getFileName())){
            file.setFileName(params.getFileName());
            if (!FileUtil.extName(params.getFileName()).equals(file.getExtName())){
                throw new BusinessException(HttpStatus.BAD_REQUEST,"请输入完整文件名");
            }
        }
        // 如果存储桶不为空
        if (Validator.isNotEmpty(bucketName)){
            file.setBucketName(params.getBucketName());
        }else {
            file.setBucketName(properties.getBucketName());
        }

        // 不异步执行上传
        String objectName = file.getMd5() +"." + file.getExtName();
        // 如果前缀不为空
        if (Validator.isNotEmpty(prefix)){
            objectName = prefix + objectName;
            file.setPrefix(prefix);
        }
        file.setObjectName(objectName);
        // 存入
        s3Client.putObject(file.getBucketName(), objectName, params.getFile());

        // 返回结果
        return fileService.saveOne(file);
    }

    @Override
    public UfsFile uploadFile(FileUploadParams params) {
        return upload(params, params.getPrefix());
    }

    @Override
    public UfsFile removeFile(String fileId) {
        UfsFile file = fileService.getOneUnwrap(fileId);
        fileService.deleteOne(fileId);
        s3Client.deleteObject(file.getBucketName(), file.getObjectName());
        return file;
    }

    @Override
    @SneakyThrows
    public UfsFile updateFile(String fileId, InputStream stream) {
        // 用字符串作为中转
        String content = IoUtil.readUtf8(stream);
        stream = IoUtil.toUtf8Stream(content);
        // 简单初始化
        UfsFile oldFile = fileService.getOneUnwrap(fileId);
        UfsFile newFile = new UfsFile();
        BeanUtil.copyProperties(oldFile, newFile, CopyOptions.create().setIgnoreError(true).setIgnoreNullValue(true));
        newFile.setFileSize((long) stream.available());
        // 计算 MD5
        String md5 = SecureUtil.md5(stream);
        String prefix = oldFile.getPrefix();
        // 不异步执行上传
        String objectName = md5 +"." + oldFile.getExtName();
        // 如果前缀不为空
        if (Validator.isNotEmpty(prefix)){
            objectName = prefix + objectName;
        }
        newFile.setObjectName(objectName);
        newFile.setMd5(md5);
        // 文件上传
        String bucketName = oldFile.getBucketName();
        stream = IoUtil.toUtf8Stream(content);
        s3Client.putObject(bucketName, objectName, stream);
        // 发布事件
        context.publishEvent(new UfsFileUpdateEvent(this, newFile, oldFile));
        return fileService.updateOne(newFile);
    }

    @Override
    @SneakyThrows
    public UfsFile updateFile(String fileId, MultipartFile multipartFile) {
        // 简单初始化
        UfsFile file = UfsUtil.buildFile(multipartFile);
        UfsFile oldFile = fileService.getOneUnwrap(fileId);
        UfsFile newFile = new UfsFile();
        BeanUtil.copyProperties(oldFile, newFile, CopyOptions.create().setIgnoreError(true).setIgnoreNullValue(true));
        newFile.setFileSize(file.getFileSize());
        newFile.setFileName(file.getFileName());
        if (!file.getExtName().equals(oldFile.getExtName())){
            throw new BusinessException(HttpStatus.BAD_REQUEST,"更新文件类型必需一致");
        }
        InputStream stream = multipartFile.getInputStream();
        // 计算 MD5
        String md5 = SecureUtil.md5(stream);
        String prefix = oldFile.getPrefix();
        // 不异步执行上传
        String objectName = md5 +"." + oldFile.getExtName();
        // 如果前缀不为空
        if (Validator.isNotEmpty(prefix)){
            objectName = prefix + objectName;
        }
        newFile.setObjectName(objectName);
        newFile.setMd5(md5);
        // 文件上传
        String bucketName = oldFile.getBucketName();
        s3Client.putObject(bucketName, objectName, stream);
        // 发布事件
        context.publishEvent(new UfsFileUpdateEvent(this, file, oldFile));
        return fileService.updateOne(newFile);
    }

    @Override
    public InputStream getFileStream(String fileId) {
        UfsFile file = fileService.getOneUnwrap(fileId);
        return s3Client.getObjectStream(file.getBucketName(), file.getObjectName());
    }

    @Override
    public ResponseEntity<byte[]> download(UfsFile file, HttpServletRequest request) {
        InputStream stream = s3Client.getObjectStream(file.getBucketName(), file.getObjectName());

        return fileService.downloadMethod(stream,request,file);
    }

    @Override
    public ResponseEntity<byte[]> download(String fileId, HttpServletRequest request) {
        UfsFile file = fileService.getOneUnwrap(fileId);
        return download(file, request);
    }

    @Override
    public ResponseEntity<byte[]> open(UfsFile file, HttpServletRequest request) {

        InputStream stream = s3Client.getObjectStream(file.getBucketName(), file.getObjectName());

        return fileService.previewMethod(stream,request,file);
    }

    @Override
    public ResponseEntity<byte[]> open(String fileId, HttpServletRequest request) {
        UfsFile file = fileService.getOneUnwrap(fileId);
        return open(file, request);
    }
}
